package EP::Visualization::AnnotationGraphic;

use strict;

use Data::UUID;
use Tie::IxHash;
use POSIX;
use GD;

use EP::Visualization::Graphics;
use EP::Visualization::Hooks;

our @ISA = qw ( EP::Visualization::Graphics );

sub setData {
  my $self = shift;
  my %params = @_;

  die "No data supplied to AnnotationGraphic::setData" if not exists $params{data};

  $self->{data}        = $params{data};

  $self->{'png_draw_annot_ids'} = $params{'png_draw_annot_ids'} if $params{'png_draw_annot_ids'};
  $self->{'font-family'} = $params{'font-family'} if $params{'font-family'};
  $self->{'font-size'}   = $params{'font-size'}   if $params{'font-size'};
  $self->{'font-style'}  = $params{'font-style'}  if $params{'font-style'};
  $self->{'font-weight'} = $params{'font-weight'} if $params{'font-weight'};
  $self->{'annot-width'} = $params{'annot-width'} if $params{'annot-width'};

  $self->{'no-color-efs'} = $params{'no-color-efs'} if $params{'no-color-efs'};

  return;
}

###########################################################
#set the graphics object by which the data                #
#should be ordered to                                     #
###########################################################

sub orderBy {
  my $self        = shift;
  my $graphicsObj = shift;
  $self->{orderBy} = $graphicsObj;
  return;
}

#########################################################
#set the hooks                                          #
#########################################################

sub setHook {
  my $self = shift;
  my $hook = shift;
  $self->{hook} = $hook;
  return;
}

#########################################################
#get the hooks                                          #
#########################################################

sub getHook {
  my $self = shift;
  return $self->{hook};
}

#########################################################
#returns string identifying the object as a annotation  #
#########################################################

sub getType {
  return "annotation";
}

sub getWidth {
  my $self = shift;

  return ($#{ $self->getData() } + 1) * $self->{'font-size'} if $self->getOrientation() != 0;
  return 150;
}

sub getHeight {
  my $self = shift;

  return 150 if $self->getOrientation() != 0;
  return ($#{ $self->getData() } + 1) * $self->{'font-size'};
}

##########################################################
#reorder annotation according to hook of graphics object #
#to order to                                             #
##########################################################
sub order {
  my $self = shift;
  if ( exists $self->{orderBy} ) {
    my $graphicsObj = $self->{orderBy};
    my @data        = @{ $self->getData() };
    my @orderedHook = @{ $graphicsObj->getHook->getHookSet() };

    if ( $graphicsObj->getOrientation() == -90
	 || $graphicsObj->getOrientation() == 180 ) {
      @orderedHook = reverse @orderedHook;
    }

    my @orderedData = @data[@orderedHook];

    $self->setData( data => \@orderedData, 'font-size' => $self->{'font-size'} );

    my $hook = new EP::Visualization::Hooks( \@orderedHook );
    $self->setHook($hook);
  }
}

##########################################################
#add the annotation svg elements to the svg initiated in #
#the Grid object                                         #
##########################################################

sub to_svg {
  my $self      = shift;
  my ( $svg, $height, $width, $xposition, $yposition ) = @_;
  $self->order();		#order data before getting it!!

  my $fontsize = $self->{'font-size'} || 12;
  return $svg if $fontsize == 2;

  my @data = @{ $self->getData() };

  my $fieldHeight;
  my $rotate = $self->getOrientation();

  my $ug = new Data::UUID;
  my $annot_set_id = $ug->create_str();
  $annot_set_id =~ s/-/_/g;	# change all dashes to underscores in UUID
  my @annot_names = $data[0]->{full_annot}->Keys;

  $svg->script ( type                      => "text/javascript",
		 "a3:scriptImplementation" => "Adobe" )->cdata ( "var sa_$annot_set_id = 'switch_annot_$annot_set_id';" );

  if ( @annot_names > 1) {
    my $btn = $svg->addNewChild ( "", "gui:button" );
    $btn->setAttribute ( "mode" => "toggle" );
    $btn->setAttribute ( "x", ( $rotate != 0 ) ? $xposition - 12 : $xposition + 5 );
    $btn->setAttribute ( "y", ( $rotate != 0 ) ? $yposition + 5   : $yposition - 12 );
    $btn->setAttribute ( "width", 10 );
    $btn->setAttribute ( "height", 10 );
    $btn->setAttribute ( "label", "A" );
    $btn->setAttribute ( "callback", "toggle_display(sa_$annot_set_id)" );
  }

  foreach my $annot_id ( 0 .. $#annot_names ) {
    my $scale_w = 1;#$width  / ( $fontsize * $self->max_length_annot ( \@data, $annot_names[$annot_id] ) );
    my $scale_h = $height / ( $fontsize * @data );

    my $transform = "translate($xposition,$yposition)";
    if ( $rotate == -90 ) {
      my $xtrans = - ( $height + $yposition );
      my $ytrans = $xposition;

      $transform = "rotate($rotate) translate($xtrans,$ytrans)";
    }

    my $cp = $svg->clipPath ( id =>  "annot_" . $annot_set_id . "_" . $annot_id . "_clip" );
    $cp->rect ( 'x' => 0, 'y' => 0, width => $width - 10,  height => $height ) if $rotate == 0;
    $cp->rect ( 'x' => 0, 'y' => 0, width => $height - 10, height => $width  ) if $rotate == -90;

    my $group = $svg->group ( id => "annot_" . $annot_set_id . "_" . $annot_id,
			      transform => $transform,
			      style => { display => ( $annot_id > 0 ) ? "none" : "block" },
			      'clip-path' => "url(#annot_" . $annot_set_id . "_" . $annot_id . "_clip)"
			    );

    if ( $annot_id == 0 ) {
      $svg -> script ( type                      => "text/javascript",
		       "a3:scriptImplementation" => "Adobe" )->cdata ( "activeannot['$annot_set_id'] = 0" );
    }

    for my $i ( 0 .. $#data ) {
      my $annot_text = $data[$i]->{full_annot}->Values ( $data[$i]->{full_annot}->Indices ( $annot_names[$annot_id] ) );
      my $a = $group->anchor ( "xlink:href"   => "http://www.ncbi.nlm.nih.gov/gquery/gquery.fcgi?term=$annot_text",
			       "target"   => '_blank' );

      (my $tooltip_text = $annot_text) =~ s/'/\\'/g;

      $a->text ( 'x' => 6,
		 'y' => $fontsize * ( $i + 1 ),
		 'font-size' => $fontsize,
		 'font-family' => "fixed",
		 onmouseover => "ShowTooltip('$tooltip_text', evt)",
		 onmouseout => "HideTooltip()"
	       )->cdata ( $annot_text );
    }
  }

  my $g = $svg->group ( id => "switch_annot_$annot_set_id", style => "display: none;", transform => "translate(10,5)" );

  my $annot_width = 0;
  foreach ( @annot_names ) {
    $annot_width = length $_ if length $_ > $annot_width;
  }

  $g->rect ( 'x' => ( $rotate != 0 ) ? $xposition - 7 : $xposition + 5,
	     'y' => ( $rotate != 0 ) ? $yposition + 12 : $yposition - 7,
	     width => $annot_width * 10,
	     height => 5 + 12 * @annot_names,
	     fill => 'lightblue',
	     rx => 4,
	     ry => 4 );
  foreach my $annot_id ( 0 .. $#annot_names ) {
    my $btn = $g->addNewChild ( "", "gui:radiobutton" );
    $btn->setAttribute ( "group", "annot_$annot_set_id" );
    $btn->setAttribute ( "x",( $rotate != 0 ) ? $xposition : 10 + $xposition );
    $btn->setAttribute ( "y", ( $rotate != 0 ) ? 12 * $annot_id + $yposition + 17 : 12 * $annot_id + $yposition );
    $btn->setAttribute ( "label", $annot_names[$annot_id] );
    $btn->setAttribute ( "callback", "setActiveAnnot(/$annot_set_id/.source,$annot_id);" );
  }
  return $svg;
}

sub to_png {
  my $self      = shift;
  my ( $png, $height, $width, $xposition, $yposition ) = @_;
  $self->order();

  my $fontsize = $self->{'font-size'} || 12;
  return $png if $fontsize < 8;

  if ( $self->getOrientation() == 0 ) {
    ( $width, $height ) = ( $height, $width );
  }

  my $gd_annotplot = $self->draw_png_annotplot ( $height, $width );

  if ( $self->getOrientation() != 0 ) {
    $gd_annotplot = $gd_annotplot->copyRotate270();
  } else {
    ( $width, $height ) = ( $height, $width );
  }

  $png->copy ( $gd_annotplot, $xposition, $yposition, 0, 0, $width, $height );

  return $png;
}

sub draw_png_annotplot {
  my $self = shift;
  my ( $width, $height ) = @_;

  my $png = new GD::Image ( $width, $height );
  my $white = $png->colorAllocate ( 255, 255, 255 );
  my $black = $png->colorAllocate ( 0, 0, 0 );

  my @data = @{ $self->getData() };

  my $fieldHeight;
  my @annot_names = $data[0]->{full_annot}->Keys;
  my @draw_annot_ids = @{$self->{png_draw_annot_ids}} if exists $self->{png_draw_annot_ids};
  @draw_annot_ids = 0 .. $#annot_names if not exists $self->{png_draw_annot_ids};

  my @colors = distinctColors ( 200 ) if !$self->{'no-color-efs'};
  my %u;

  my $shouldswap = ( $self->getOrientation() != 0);

  for my $i ( 0 .. $#data ) {
    my $annot_text;

    foreach my $annot_id ( @draw_annot_ids ) {
      $annot_text .= ";" if $annot_text;
      my $this_annot_text = $data[$i]->{full_annot}->Values ( $data[$i]->{full_annot}->Indices ( $annot_names[$annot_id] ) );
      $annot_text .= " " . $this_annot_text;

      if ( !$self->{'no-color-efs'} ) {
	$u{$this_annot_text} = $png->colorResolve ( 255, 255, 255 ) if not exists $u{$this_annot_text} and $this_annot_text eq "";
	my $color = shift @colors;
	warn "Ran out of colours at $this_annot_text - remainder will be white\n!" and $color = [ 255, 255, 255 ] if not $color;
	$u{$this_annot_text} = $png->colorAllocate ( @{$color} ) if not exists $u{$this_annot_text};
      }
    }

    my $max_length = 150/6 - 4;

    my $title = substr ( $annot_text, 1 );
    $title =~ s/[[:^alnum:]]/ /g;

    my $coords;
    if ( $shouldswap )
        {
        $coords = join ( ",", $self->{'font-size'} * $i, 0, $self->{'font-size'} * ($i + 1), 150 );
        }
    else
        {
        $coords = join ( ",", 0, $self->{'font-size'} * $i, 150, $self->{'font-size'} * ($i + 1) );
        }

    push @{$self->{image_map}}, { shape  => "rectangle",
				  coords => $coords,
				  title  => $title,
				  href => "#"
				};

    $annot_text = substr ( $annot_text, 0, $max_length ) . "... " if length ( $annot_text ) > $max_length + 4;

    if ( !$self->{'no-color-efs'} ) {
      my $color = $white;
      $color = $u{$data[$i]->{full_annot}->Values ( $data[$i]->{full_annot}->Indices ( $annot_names[$draw_annot_ids[0]] ) ) } if scalar @draw_annot_ids == 1;
      $png->filledRectangle ( 0, $self->{'font-size'} * $i, $height, $self->{'font-size'} * ($i + 1), $color );
    }

    $png->string ( gdSmallFont, 0, $self->{'font-size'} * $i + 0.5 * $self->{'font-size'} - 6, $annot_text, $black );
  }

  warn "$_: " . join ( ",", $png->rgb($u{$_})) . "\n" foreach keys %u;
  return $png;
}

# sub: distinctColors
#   Returns n distinct (rgb) colors
# params:
#   n - number of colors to generate
sub distinctColors {
  my ($n) = @_;

  my $nn = 1;
  my $hue_increment = 0.15;
  my $s = 0.5; # constant saturation

  my $lum_steps = $n * $hue_increment;
  my $int_lum_steps = int ( $lum_steps );

  if ( $lum_steps > $int_lum_steps ) {
    $lum_steps = $int_lum_steps + 1;
  }

  my $lum_increment = 0.3 / $lum_steps;

  my @rc;

  for ( my $l = 1.0; $l > 0.7; $l -= $lum_increment ) {
    for ( my $h = 0.0; $h < 1.0; $h += $hue_increment ) {
      my ( $r, $g, $b ) = hls2rgb ( $h, $l, $s );
      push @rc, [ int(255 * $r), int(255 * $g), int(255 * $b) ];
      $nn++;
      return @rc if $nn > $n;
    }
  }
}

sub hls2rgb {
  my ( $h, $l, $s ) = @_;
  # h, l and s are floats between 0.0 and 1.0, ditto for r, g and b
  # h = 0   => red
  # h = 1/3 => green
  # h = 2/3 => blue

  my $h6 = ($h - POSIX::floor ($h)) * 6;
  my $r = $h6 <= 3 ? 2 - $h6 : $h6 - 4;
  my $g = $h6 <= 2 ? $h6 : $h6 <= 5 ? 4 - $h6 : $h6 - 6;
  my $b = $h6 <= 1 ? -$h6 : $h6 <= 4 ? $h6 - 2 : 6 - $h6;

  $r = $r < 0.0 ? 0.0 : $r > 1.0 ? 1.0 : $r;
  $g = $g < 0.0 ? 0.0 : $g > 1.0 ? 1.0 : $g;
  $b = $b < 0.0 ? 0.0 : $b > 1.0 ? 1.0 : $b;

  $r = (($r-1)*$s+1)*$l;
  $g = (($g-1)*$s+1)*$l;
  $b = (($b-1)*$s+1)*$l;

  return ( $r, $g, $b );
}

sub colorComplement {
  my ($r, $g, $b) = @_;

  $r = ( -$r - 1 ) % 256;
  $g = ( -$g - 1 ) % 256;
  $b = ( -$b - 1 ) % 256;

  return ( $r, $g, $b );
}

sub max_length_annot {
  my $self = shift;
  my ($data, $annot_name) = @_;

  my $max_len = 1;

  if ( !$annot_name ) {
    my @annot_names = $$data[0]->{full_annot}->Keys;
    my @draw_annot_ids = 0 .. $#annot_names;

      # = @{$self->{png_draw_annot_ids}} if exists $self->{png_draw_annot_ids};
      #    @draw_annot_ids = 0 .. $#annot_names if not exists $self->{png_draw_annot_ids};

    for my $i ( 0 .. $#{$data} ) {
      my $annot_text;

      foreach my $annot_id ( @draw_annot_ids ) {
	if ( exists $self->{png_draw_annot_ids} ) {
	  $annot_text = " $i. $$data[$i]->{full_annot}->{$annot_names[$annot_id]}";
	} else {
	  $annot_text .= ";" if $annot_text;
	  $annot_text .= " $i. $$data[$i]->{full_annot}->{$annot_names[$annot_id]}";
	}

	my $l = length ( $annot_text );
	$max_len = $l if $l > $max_len;
      }
    }

    warn "@annot_names @draw_annot_ids MAX LEN: $max_len";
  } else {
    foreach my $datum ( @{$data} ) {
      my $annot_text = $datum->{full_annot}->{$annot_name};
      my $this_len = length ( $annot_text );
      $max_len = $this_len if $this_len > $max_len;
    }
  }

  return $max_len + 1;
}

1;
